package com.yst.sqlbuilder;

import com.yst.lambda.LambdaFuncUtils;
import com.yst.lambda.TableFieldFunc;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

public class SQLUpdate<T> implements ISQLExpression {
    @Override
    public String generateExpression(GenerateParam param) {
        String tableName="";
        if (Objects.nonNull(clazz)) {
            tableName = getEntitiesConfig().getTableName(clazz);
        }

        if (!sets.isEmpty()){
            Optional<TableFieldFunc<T,?>> fieldFuncOptional=sets.keySet().stream().findFirst();
            clazz= LambdaFuncUtils.getOwnerClass(fieldFuncOptional.get());
            tableName = getEntitiesConfig().getTableName(clazz);
        }

        String whereSQL=updateCondition.generateExpression(param).trim();
        return "UPDATE".concat("\r\n").concat(StringUtils.leftPad(tableName,tableName.length()+2," "))
                .concat("\r\n").concat("SET\r\n")
                .concat(generateSetExpression(param)).concat("\r\n")
                .concat("WHERE\r\n")
                .concat(StringUtils.leftPad(whereSQL,whereSQL.length()+2," "));
    }
    private Class<?> clazz;
    public SQLUpdate<T> update(Class<T> clazz){
        this.clazz=clazz;
        return this;
    }
    private String generateSetExpression(GenerateParam param){
        List<StringBuilder> sqlList=new ArrayList<>();
        for(TableFieldFunc<T,?> field:sets.keySet()){
            StringBuilder stringBuilder=new StringBuilder();
            String realFieldName=getEntitiesConfig().getFieldName(field);
            Object value=sets.get(field);
            if (Objects.isNull(value)){
                stringBuilder.append(realFieldName).append("=null");
            }
            else if (value instanceof CharSequence){
                stringBuilder.append(realFieldName).append("='").append(value).append("'");
            }
            else if (value instanceof SQLParam){
                stringBuilder.append(realFieldName).append("=").append(((SQLParam)value).generateExpression(param));
            }
            else {
                stringBuilder.append(realFieldName).append("=").append(value);
            }
            sqlList.add(stringBuilder);
        }
        String result=sqlList.stream().map(StringBuilder::toString).collect(Collectors.joining(",\r\n  "));
        return StringUtils.leftPad(result,result.length()+2," ");
    }
    private final Map<TableFieldFunc<T,?>,Object> sets=new HashMap<>();
    public <R> SQLUpdate<T> set(TableFieldFunc<T,R> field,R value){
        sets.put(field,value);
        return this;
    }
    public <R> SQLUpdate<T> setExp(TableFieldFunc<T,R> field, SQLParam<R> value){
        sets.put(field,value);
        return this;
    }
    public static final class SQLUpdateCondition<T> extends SQLCondition<T>{

        private final SQLUpdate<T> sqlUpdate;
        SQLUpdateCondition(SQLUpdate<T> sqlUpdate){
            this.sqlUpdate=sqlUpdate;
        }

        public SQLUpdate<T> getOwnerUpdate(){
            return sqlUpdate;
        }

        @Override
        public SQLUpdateCondition<T> compare(TableFieldFunc<T, ?> field, Symbol symbol, Object value) {
            return (SQLUpdateCondition<T>)super.compare(field, symbol, value);
        }

        @Override
        public SQLUpdateCondition<T> equal(TableFieldFunc<T, ?> field, Object value) {
            return (SQLUpdateCondition<T>)super.equal(field, value);
        }

        @Override
        public SQLUpdateCondition<T> notEqual(TableFieldFunc<T, ?> field, Object value) {
            return (SQLUpdateCondition<T>)super.notEqual(field, value);
        }

        @Override
        public SQLUpdateCondition<T> less(TableFieldFunc<T, ?> field, Object value) {
            return (SQLUpdateCondition<T>)super.less(field, value);
        }

        @Override
        public SQLUpdateCondition<T> lessEqual(TableFieldFunc<T, ?> field, Object value) {
            return (SQLUpdateCondition<T>)super.lessEqual(field, value);
        }

        @Override
        public SQLUpdateCondition<T> more(TableFieldFunc<T, ?> field, Object value) {
            return (SQLUpdateCondition<T>)super.more(field, value);
        }

        @Override
        public SQLUpdateCondition<T> moreEqual(TableFieldFunc<T, ?> field, Object value) {
            return (SQLUpdateCondition<T>)super.moreEqual(field, value);
        }

        @Override
        public SQLUpdateCondition<T> in(TableFieldFunc<T, ?> field, Collection<?> values) {
            return (SQLUpdateCondition<T>)super.in(field, values);
        }

        @Override
        public SQLUpdateCondition<T> between(TableFieldFunc<T, Number> field, Number value1, Number value2) {
            return (SQLUpdateCondition<T>)super.between(field, value1, value2);
        }

        @Override
        public SQLUpdateCondition<T> between(TableFieldFunc<T, Date> field, Date value1, Date value2) {
            return (SQLUpdateCondition<T>)super.between(field, value1, value2);
        }

        @Override
        public SQLUpdateCondition<T> between(TableFieldFunc<T, ?> field, SQLParam<?> value1, SQLParam<?> value2) {
            return (SQLUpdateCondition<T>)super.between(field, value1, value2);
        }

        @Override
        public SQLUpdateCondition<T> in(TableFieldFunc<T, ?> field, Object... values) {
            return (SQLUpdateCondition<T>)super.in(field, values);
        }

        @Override
        public SQLUpdateCondition<T> like(TableFieldFunc<T, String> field, String value) {
            return (SQLUpdateCondition<T>)super.like(field, value);
        }

        @Override
        public SQLUpdateCondition<T> likeLeft(TableFieldFunc<T, String> field, String value) {
            return (SQLUpdateCondition<T>)super.likeLeft(field, value);
        }

        @Override
        public SQLUpdateCondition<T> likeRight(TableFieldFunc<T, String> field, String value) {
            return (SQLUpdateCondition<T>)super.likeRight(field, value);
        }
    }
    private final SQLUpdateCondition<T> updateCondition=new SQLUpdateCondition<>(this);
    public SQLUpdateCondition<T> where(){
        return updateCondition;
    }

}
